home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 February: Tool Chest / Dev.CD Feb 97 TC.toast / Sample Code / Games / MoofWars / MoofWars Sprocket 8⁄15⁄96 / •Source / Command.cp next >
Encoding:
Text File  |  1996-08-19  |  9.6 KB  |  336 lines  |  [TEXT/CWIE]

  1. /*************************************************************************************
  2. #
  3. #    Command.cp
  4. #    
  5. #    The command section accomplishes two things for the game -- first, providing a
  6. #    standard clock to which all actions can be synchonized, and second, grabbing the state
  7. #    of the keyboard to pass along to those game parts that need it.
  8. #
  9. #    This code implements a VBL take that goes out and grabs the state of the keyboard once
  10. #    per frame of animation.  To minimize the hit, all this does is save this command state
  11. #    off into a buffer and then exit.
  12. #
  13. #    We create a buffer for two seconds worth of commands, although in practice we want to
  14. #    pull commands from the buffer much faster than this.
  15. #
  16. #    Eventually, this code will also probably need to deal with networking tasks.
  17. #
  18. #    Author: Timothy Carroll
  19. #    Apple Developer Technical Support
  20. #    timc@apple.com
  21. #
  22. #    Modification History: 
  23. #
  24. #    8/15/96        TMC     Initial Release             
  25. #
  26. #    Copyright © 1996 Apple Computer, Inc., All Rights Reserved
  27. #
  28. #
  29. #    You may incorporate this sample code into your applications without
  30. #    restriction, though the sample code has been provided "AS IS" and the
  31. #    responsibility for its operation is 100% yours.  However, what you are
  32. #    not permitted to do is to redistribute the source as "DSC Sample Code"
  33. #    after having made changes. If you're going to re-distribute the source,
  34. #    we require that you make it clear in the source that the code was
  35. #    descended from Apple Sample Code, but that you've made changes.
  36. #
  37. *************************************************************************************/
  38.  
  39.  
  40. /******************************************************************************************
  41.     Other notes:
  42.     
  43.     1. The 68K version of the code must store an A5 context in an expanded VBL record so that
  44.     the VBL task can call up information.  The PowerPC System Software defines a slightly
  45.     different version of the record and uses standard fragment RTOCs to get at the application
  46.     context.
  47.  
  48.     2. The data buffer must be locked and a fixed size to prevent memory from being moved around
  49.     in the VBL task.
  50.     
  51.     3. We create the task in the application heap so that it will be cleared automatically if the
  52.     app dies.  If we change it to a system level task, then we need to patch ExitToShell to make
  53.     sure we dispose of it before exiting the app.
  54.     
  55. ******************************************************************************************/
  56.  
  57. #include <Retrace.h>
  58. #include <Errors.h>
  59.  
  60. #include "Command.h"
  61.  
  62.  
  63. // First, the record that will hold our running VBL task.
  64.  
  65. typedef struct CommandVBLRec *CommandVBLRecPtr;
  66.  
  67. #if GENERATING68K
  68. // The VBL task structure
  69. struct CommandVBLRec {
  70.     VBLTask        myVBLTask;
  71.     long        vblA5;
  72.     Boolean        running;
  73. };
  74. // and the VBL routines -- GetVBLRec is used to retrieve our record, since a 68K task passes
  75. // the data in via a register rather than on the stack.
  76. CommandVBLRecPtr GetVBLRec (void) = 0x2008;              /*MOVE.L A0,D0*/
  77. void CommandVBL (void);
  78.  
  79. #else
  80. // The VBL task structure
  81. struct CommandVBLRec {
  82.     VBLTask        myVBLTask;
  83.     Boolean        running;
  84. };
  85.  
  86. // and the VBL routine
  87. void CommandVBL (VBLTaskPtr recPtr);
  88.  
  89. #endif
  90.  
  91.  
  92.  
  93. // Need to define a UPP for the command.
  94. static VBLUPP CommandVBLUPP = NewVBLProc (CommandVBL);
  95.  
  96.  
  97.  
  98.  
  99. // these variables are set by the frames desired passed in by the game.
  100. static short sFramesDesired = 0;
  101. static short sSizeOfBuffer = 0;
  102. static TCommand *sCommandBuffer = NULL;
  103.  
  104. // These hold the locations that we'll store and retrieve new commands from.
  105. static short sInCommandIndex = 0;
  106. static short sOutCommandIndex = 0;
  107.  
  108. // Finally, the data structure used by the VBL itself.  I suppose we could add all of
  109. // our elements to the CommandVBLRec, which would allow us multiple command tasks.  Why
  110. // would we need to though?
  111.  
  112. static CommandVBLRec sCommandVBL;
  113.  
  114.  
  115.  
  116. /******************************************************************************************
  117.     InitializeCommands
  118.     
  119.     This routine fills in and allocates all structures required for a new command task.
  120.     It returns an error if you try calling InitializeCommands twice in a row, or if it
  121.     can't allocate the memory it needs to run the task.
  122.  
  123. ******************************************************************************************/
  124.  
  125. OSErr InitializeCommands (short framesDesired)
  126. {
  127.     OSErr theErr = noErr;
  128.     
  129.     if (sCommandBuffer != NULL)
  130.         return paramErr;
  131.     
  132.     // Allocate the command buffer. 2 seconds worth of room in the buffer should be enough
  133.     sFramesDesired = framesDesired;
  134.     sSizeOfBuffer = 120 / framesDesired;
  135.     sInCommandIndex = 0;
  136.     sOutCommandIndex = 0;
  137.     
  138.     sCommandBuffer = (TCommand *) NewPtr (sizeof (TCommand) * sSizeOfBuffer);
  139.     
  140.     theErr = MemError();
  141.     
  142.     FAIL_OSERR (theErr, "\pUnable to allocate the command buffer")
  143.     FAIL_NIL (sCommandBuffer, "\pUnable to allocate the command buffer")
  144.     
  145.  
  146.     // create and install the VBL task.
  147.     
  148.     sCommandVBL.myVBLTask.qType = vType;
  149.     sCommandVBL.myVBLTask.vblAddr = CommandVBLUPP;
  150.     sCommandVBL.myVBLTask.vblCount = 0; // we'll turn it on when we start accepting commands.
  151.     sCommandVBL.myVBLTask.vblPhase = 0;
  152.     sCommandVBL.running = false;
  153.  
  154. #if GENERATING68K
  155.     sCommandVBL.vblA5 = GetCurrentA5();
  156. #endif
  157.     
  158.     theErr = VInstall((QElemPtr) &sCommandVBL);
  159.     
  160.     FAIL_OSERR (theErr, "\pUnable to install the VBL task")
  161.     
  162.     return noErr;
  163.     
  164.     error:
  165.     
  166.     if (sCommandBuffer != NULL)
  167.         DisposePtr ((Ptr)sCommandBuffer);
  168.     sCommandBuffer = NULL;
  169.     
  170.     if (theErr == noErr)
  171.         theErr = paramErr;
  172.     return theErr;
  173. }
  174.  
  175. /******************************************************************************************
  176.     ExitCommands
  177.     
  178.     Clean up all the stuff we did when we built this task.
  179.  
  180. ******************************************************************************************/
  181.  
  182.  
  183. OSErr ExitCommands (void)
  184. {
  185.     // We'll stop this task if it hasn't been already.
  186.     StopAcceptingCommands();
  187.     
  188.     DisposePtr ((Ptr) sCommandBuffer);
  189.     sCommandBuffer = NULL;
  190.     // remove the VBL task and throw it away.
  191.     return VRemove((QElemPtr) &sCommandVBL);
  192.  
  193. }
  194.  
  195. /******************************************************************************************
  196.     FlushCommandBuffer
  197.     
  198.     This empties any existing commands in the buffer.  Only call it when the task is not
  199.     running.
  200.  
  201. ******************************************************************************************/
  202.  
  203. void FlushCommandBuffer (void)
  204. {
  205. #if qDebugging
  206.     if (sCommandVBL.running)
  207.     // Need to make sure the VBL task is stopped when we do this.
  208.         DebugStr("\pCalled FlushCommandBuffer while task was running!");
  209. #endif
  210.     
  211.     sInCommandIndex = sOutCommandIndex = 0;
  212. }
  213.  
  214.  
  215.  
  216. /******************************************************************************************
  217.     StartAcceptingCommands
  218.  
  219. ******************************************************************************************/
  220.  
  221.  
  222. void StartAcceptingCommands (void)
  223. {    
  224. #if qDebugging
  225.     if (sCommandVBL.running)
  226.         DebugStr("\pCalled StartAcceptingCommands on an already running task!");
  227. #endif
  228.  
  229.     // start up the VBL task!
  230.     sCommandVBL.myVBLTask.vblCount = sFramesDesired;
  231.     sCommandVBL.running = true;
  232. }
  233.  
  234.  
  235. /******************************************************************************************
  236.     StopAcceptingCommands
  237.  
  238. ******************************************************************************************/
  239.  
  240. void StopAcceptingCommands (void)
  241. {
  242.     // stop the VBL task
  243.     sCommandVBL.myVBLTask.vblCount = 0;
  244.     sCommandVBL.running = false;
  245. }
  246.  
  247.  
  248.  
  249. /******************************************************************************************
  250.     IsCommandWaiting
  251.  
  252. ******************************************************************************************/
  253. Boolean IsCommandWaiting (void)
  254. {
  255.     if (sInCommandIndex != sOutCommandIndex)
  256.         return true;
  257.     else
  258.         return false;
  259. }
  260.  
  261.  
  262.  
  263. /******************************************************************************************
  264.     RetrieveCommand
  265.     
  266.     Retrieves the next command from the buffer, returning false if there actually isn't a
  267.     command available.
  268.     
  269. ******************************************************************************************/
  270. Boolean RetrieveCommand (TCommand *outCommand)
  271. {
  272.     if (sInCommandIndex != sOutCommandIndex)
  273.     {
  274.         sOutCommandIndex = (sOutCommandIndex+1) % sSizeOfBuffer;
  275.         *outCommand = sCommandBuffer[sOutCommandIndex];
  276.         return true;
  277.     }
  278.     else
  279.         return false;
  280.     
  281. }
  282.  
  283.  
  284.  
  285.  
  286. // Now for the task itself.  The code is a little ugly because of the conditionalized
  287. // stuff for 68K.  Basically we set up the task, do our magic and then tear everything down.
  288.  
  289. #if GENERATING68K
  290. // On classic 68K macs, we need to retrieve our stored A5 and get the pointer to our VBL
  291. // task record.
  292. void CommandVBL (void)
  293. {
  294.     CommandVBLRecPtr    recPtr = GetVBLRec();              /*pointer to task record*/
  295.     long                curA5  = SetA5(recPtr->vblA5);   /*stored value of A5*/
  296.     
  297. #else
  298. // On powermacs, we can just open up the task and go!
  299. void CommandVBL (VBLTaskPtr recPtr)
  300. {
  301. #endif
  302.  
  303.  
  304.  
  305.     // Finally we actually can do our work.
  306.  
  307.      sInCommandIndex = (sInCommandIndex+1) % sSizeOfBuffer;
  308.  
  309.      // If InCommandIndex is equal to the OutCommandIndex, it means
  310.      // we've overlapped completely and we should throw out the new
  311.      // command, so we throw out the new command rather than overwriting it.
  312.      // Ideally, the game engine should be fast enough that this should never happen.
  313.  
  314.      if (sInCommandIndex != sOutCommandIndex)
  315.      {
  316.          // Get the keys and stuff them into the next available slot.
  317.          // We're actually using the variant array syntax in C here.
  318.          GetKeys((sCommandBuffer[sInCommandIndex]).keys);
  319.      }
  320.      else
  321.      {
  322.          // After decrementing, sInCommandIndex could conceivably be -1.  But if you
  323.          // look above, we always add one, so we won't ever use the negative address.
  324.          sInCommandIndex--;
  325.      }
  326.  
  327.     // Reset vblCount so that this procedure executes again.
  328.    // And of course, on a 68K mac we need to clean up after ourselves.
  329. #if GENERATING68K
  330.     recPtr->myVBLTask.vblCount = sFramesDesired;
  331.     (void) SetA5(curA5);
  332. #else
  333.     recPtr->vblCount = sFramesDesired;
  334. #endif
  335.  
  336.  }